#include "epd_io.h"
#include "cmsis_os2.h"
#include "pin_config.h"
#include "sl_core.h"
#include "sl_si91x_clock_manager.h"
#include "sl_gpio_board.h"
#include "sl_driver_gpio.h"
#include "sl_si91x_driver_gpio.h"
#include "sl_si91x_gspi.h"
#include "sl_si91x_gspi_common_config.h"


static volatile uint8_t s_gspi_mode = 0;     // 0：初始状态 1：硬件SPI模式 2：模拟SPI模式

static sl_si91x_gpio_pin_config_t s_epd_pwr_gpio_pin_config  = { { EPD_PWR_PORT, EPD_PWR_PIN }, GPIO_OUTPUT };
static sl_si91x_gpio_pin_config_t s_epd_rst_gpio_pin_config  = { { EPD_RST_PORT, EPD_RST_PIN }, GPIO_OUTPUT };
static sl_si91x_gpio_pin_config_t s_epd_dc_gpio_pin_config   = { { EPD_DC_PORT, EPD_DC_PIN }, GPIO_OUTPUT };
static sl_si91x_gpio_pin_config_t s_epd_sclk_gpio_pin_config = { { EPD_SCLK_PORT, EPD_SCLK_PIN }, GPIO_OUTPUT };
static sl_si91x_gpio_pin_config_t s_epd_cs_gpio_pin_config   = { { EPD_CS_PORT, EPD_CS_PIN }, GPIO_OUTPUT };
static sl_si91x_gpio_pin_config_t s_epd_busy_gpio_pin_config = { { EPD_BUSY_PORT, EPD_BUSY_PIN }, GPIO_INPUT };
static sl_si91x_gpio_pin_config_t s_epd_mosi_gpio_pin_config = { { EPD_MOSI_PORT, EPD_MOSI_PIN }, GPIO_INPUT };

static sl_gspi_handle_t s_epd_gspi_handle = NULL;

static void epd_gpio_init(void);
static void epd_gpio_deinit(void);

static void epd_gspi_init();
static void epd_gspi_deinit();
static void epd_gspi_callback_event(uint32_t event);

static void epd_gspi_simulation_mode(void);
static void epd_gspi_controller_mode(void);

CORE_irqState_t irqState;
void epd_enter_critical(void)
{
    irqState = CORE_EnterCritical();
}
void epd_exit_critical(void)
{
    CORE_ExitCritical(irqState);
}

void epd_io_init(void)
{
    epd_gspi_init();
    epd_gpio_init();
    epd_delay_ms(10);
}

void epd_spi_write_byte(uint8_t data)
{
    sl_si91x_gspi_set_slave_number(GSPI_SLAVE_0);
    sl_si91x_gspi_send_data(s_epd_gspi_handle, &data, 1);
}

void epd_spi_write_buffer(const uint8_t *data, uint32_t len)
{
    sl_si91x_gspi_set_slave_number(GSPI_SLAVE_0);
    sl_si91x_gspi_send_data(s_epd_gspi_handle, data, len);
    while(sl_si91x_gspi_get_status(s_epd_gspi_handle).busy) {
        epd_delay_ms(10);
    }
}

uint8_t epd_spi_read_byte(void)
{
    uint8_t i, data = 0xff;
    epd_gspi_simulation_mode();
    epd_pin_write(EPD_PIN_CS, EPD_GPIO_LOW); // CS0
    for (i = 0; i < 8; i++) {
        epd_pin_write(EPD_PIN_SCK, EPD_GPIO_LOW);
        data = data << 1;
        if (epd_pin_read(EPD_PIN_MOSI) == EPD_GPIO_HIGH) {
            data = data | 0x01;
        } else {
            data = data & 0xfe;
        }
        epd_pin_write(EPD_PIN_SCK, EPD_GPIO_HIGH);
    }
    epd_pin_write(EPD_PIN_SCK, EPD_GPIO_LOW);
    epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
    epd_gspi_controller_mode();
    return data;
}

void epd_delay_ms(uint16_t ms)
{
    osDelay(ms);
}

void epd_pin_write(epd_pin_t pin, epd_gpio_level_t value)
{
    if(value == EPD_GPIO_HIGH) {
        switch(pin) {
            case EPD_PIN_RST:
                sl_gpio_driver_set_pin(&s_epd_rst_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_DC:
                sl_gpio_driver_set_pin(&s_epd_dc_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_PWR:
                sl_gpio_driver_set_pin(&s_epd_pwr_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_CS:
                sl_gpio_driver_set_pin(&s_epd_cs_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_MOSI:
                sl_gpio_driver_set_pin(&s_epd_mosi_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_SCK:
                sl_gpio_driver_set_pin(&s_epd_sclk_gpio_pin_config.port_pin);
                break;
            default:
                epd_printf("epd_pin_write: Unsupported pin %d\r\n", pin);
                break;
        }
    } else {
        switch(pin) {
            case EPD_PIN_RST:
                sl_gpio_driver_clear_pin(&s_epd_rst_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_DC:
                sl_gpio_driver_clear_pin(&s_epd_dc_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_PWR:
                sl_gpio_driver_clear_pin(&s_epd_pwr_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_CS:
                sl_gpio_driver_clear_pin(&s_epd_cs_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_MOSI:
                sl_gpio_driver_clear_pin(&s_epd_mosi_gpio_pin_config.port_pin);
                break;
            case EPD_PIN_SCK:
                sl_gpio_driver_clear_pin(&s_epd_sclk_gpio_pin_config.port_pin);
                break;
            default:
                epd_printf("epd_pin_write: Unsupported pin %d\r\n", pin);
                break;
        }
    }
}

epd_gpio_level_t epd_pin_read(epd_pin_t pin)
{
    epd_gpio_level_t level = EPD_GPIO_LOW;

    switch(pin) {
        case EPD_PIN_BUSY:
            level = sl_gpio_driver_get_pin_output(&s_epd_busy_gpio_pin_config.port_pin);
            break;
        case EPD_PIN_MOSI:
            level = sl_gpio_driver_get_pin_output(&s_epd_mosi_gpio_pin_config.port_pin);
            break;
        default:
            epd_printf("epd_pin_read: Unsupported pin %d\r\n", pin);
            break;
    }

    return level;
}

static void epd_gpio_init(void)
{
  sl_status_t status;

  do {
    // Initialize the GPIOs by clearing all interrupts initially
    status = sl_gpio_driver_init();
    if (status != SL_STATUS_OK) {
      // Prints GPIO initialization fails
      epd_printf("sl_gpio_driver_init, Error code: %lu\r\n", status);
      break; // breaks if error occurs
    }
    epd_printf("GPIO driver initialization is successful \r\n");

    sl_gpio_set_configuration(s_epd_pwr_gpio_pin_config);
    sl_gpio_set_configuration(s_epd_rst_gpio_pin_config);
    sl_gpio_set_configuration(s_epd_dc_gpio_pin_config);
    sl_gpio_set_configuration(s_epd_busy_gpio_pin_config);

    sl_gpio_driver_set_pin(&s_epd_pwr_gpio_pin_config.port_pin);
    sl_gpio_driver_set_pin(&s_epd_rst_gpio_pin_config.port_pin);
    sl_gpio_driver_set_pin(&s_epd_dc_gpio_pin_config.port_pin);
  } while (false);
}

static void epd_gpio_deinit(void)
{
    // Deinitialize the GPIOs
    sl_status_t status = sl_gpio_driver_deinit();
    if (status != SL_STATUS_OK) {
        epd_printf("sl_gpio_driver_deinit, Error code: %lu\r\n", status);
        return;
    }
    epd_printf("GPIO driver deinitialization is successful \r\n");
}

static void epd_gspi_init()
{
  sl_status_t status;
  sl_gspi_control_config_t config;

  do {
    // Pass the address of void pointer, it will be updated with the address
    // of GSPI instance which can be used in other APIs.
    status = sl_si91x_gspi_init(SL_GSPI_MASTER, &s_epd_gspi_handle);
    if (status != SL_STATUS_OK) {
      epd_printf("sl_si91x_gspi_init: Error Code : %lu \n", status);
      break;
    }
    epd_printf("GSPI initialization is successful \n");

    //Configuration of all other parameters that are required by GSPI
    // gspi_configuration structure is from sl_si91x_gspi_init.h file.
    // The user can modify this structure with the configuration of
    // his choice by filling this structure.
    sl_si91x_gspi_set_slave_number(GSPI_SLAVE_0);
    status = sl_si91x_gspi_set_configuration(s_epd_gspi_handle, &config);
    if (status != SL_STATUS_OK) {
      epd_printf("sl_si91x_gspi_control: Error Code : %lu \n", status);
      break;
    }
    epd_printf("GSPI configuration is successful \n");

    // Register user callback function
    status = sl_si91x_gspi_register_event_callback(s_epd_gspi_handle, epd_gspi_callback_event);
    if (status != SL_STATUS_OK) {
      epd_printf("sl_si91x_gspi_register_event_callback: Error Code : %lu \n", status);
      break;
    }
    epd_printf("GSPI user event callback registered successfully \n");

    sl_gpio_driver_init();
    sl_gpio_set_configuration(s_epd_cs_gpio_pin_config);
    sl_gpio_driver_set_pin(&s_epd_cs_gpio_pin_config.port_pin);
  } while (false);
}

static void epd_gspi_deinit()
{
    sl_status_t status;

    // De-initializing the GSPI
    status = sl_si91x_gspi_deinit(s_epd_gspi_handle);
    if (status != SL_STATUS_OK) {
        epd_printf("sl_si91x_gspi_deinit: Error Code : %lu \n", status);
        return;
    }
    epd_printf("GSPI deinitialization is successful \n");
    s_epd_gspi_handle = NULL;
}

static void epd_gspi_callback_event(uint32_t event)
{
  switch (event) {
    case SL_GSPI_TRANSFER_COMPLETE:
        //epd_printf("GSPI transfer completed successfully \n");
        break;
    case SL_GSPI_DATA_LOST:
        //epd_printf("GSPI data lost event triggered \n");
        break;
    case SL_GSPI_MODE_FAULT:
        //epd_printf("GSPI mode fault event triggered \n");
        break;
  }
}

static void epd_gspi_simulation_mode(void)
{
    if (s_gspi_mode == 2) {
        return;
    }
    s_gspi_mode = 2;
    sl_gpio_set_configuration(s_epd_sclk_gpio_pin_config);
    sl_gpio_set_configuration(s_epd_mosi_gpio_pin_config);
}

static void epd_gspi_controller_mode(void)
{
    if (s_gspi_mode == 1) {
        return;
    }
    s_gspi_mode = 1;
    epd_gspi_deinit();
    epd_gspi_init();
}
